49 — 在 Blender 中创建场景
介绍
在本课中,我们将学习如何创建一个类似 Three.js Journey 网站首页上那种场景。我们将创建一片小小的风景,一条小径通向一个神奇的 portal(传送门),沿途还有围栏、树木、岩石等物体。完成后,你将能够运用所学知识创建出类似 Three.js Journey 上的房间。或者任何其他东西!
并且,为了让我们的场景看起来尽可能漂亮,我们将使用一种叫做烘焙(baking)的技术。
烘焙
当你在像 Blender 这样的 3D 软件中进行渲染时,结果通常比你导入到 Three.js 中的模型看起来更好,无论你多么努力地去获得完全相同的灯光和颜色。这是因为制作渲染时所使用的技术。
光线追踪(Ray Tracing)是指朝渲染的每个像素投射多条光线。这些光线从我们正在渲染的 geometry 开始。接着,它们会测试场景中每个 light 的方向,以确定 geometry 的哪部分被照亮,同时测试从 geometry 反弹出去的光线方向是否与场景中的其他物体发生碰撞。然后,对于每一次这样的碰撞,随着光线从其他物体上反弹,会投射出更多光线。如此反复多次。所有这些碰撞收集到的信息随后被计算出来,以确定该像素的最终颜色。
其目标是模拟现实中的照明,并实现诸如间接照明和柔和阴影等视觉效果。举个例子,如果你把一个红色物体放在白色物体附近,你会看到白色物体被染上了红色,因为光线从红色表面反弹到了白色表面。同样地,当红色物体的表面靠近白色物体时,你会看到红色物体看起来更亮。
这一过程会产生漂亮的写实渲染,但做一次渲染可能需要很多分钟,甚至数小时。
当我们用 WebGL 进行渲染时,我们需要尽可能快地完成,以获得良好的帧率。我们没有奢侈到可以把几分钟花在仅仅一次渲染上。因此,WebGL 中的渲染使用更廉价的技术,看起来没那么好看,但至少能保持一个不错的帧率。
烘焙的理念就是:我们把那些光线追踪渲染保存到 texture 中,然后在 WebGL 中使用它们,而不是使用 Three.js 提供的经典渲染技术。
下面是一个烘焙后 texture 的例子:
下面是在 Three.js 中的结果:
这里没有 light,也没有实时阴影。仅仅是你上面例子中看到的那个 texture 被贴到了这些 geometry 上。
这样,我们就能直接在 Mesh 上看到光线追踪的渲染结果,而且看起来非常棒。当我们在场景中移动时,性能也会非常好,因为我们所做的只是在 geometry 上显示一个 texture。
不幸的是,这也有一些缺点:
- 我们必须在 3D 软件中烘焙所有内容,这是一个漫长的过程。
- 我们必须加载这些 texture,如果你的场景很复杂、物体很多,就会需要很多 texture。这对加载不利,而且也可能导致体验开始时出现短暂卡顿,因为我们需要把这些 texture 加载到 GPU 中。
- 灯光不是动态的。我们无法移动灯光,也不能实时改变它们的强度或颜色。我们必须在 3D 软件中完成,并重新烘焙所有内容。
是否使用烘焙取决于你和你的项目。请记住,你仍然可以混合使用烘焙和非烘焙的 material,但很难保持统一的效果。
流程
要创建那个烘焙场景,我们需要经历多个步骤:
- 在 3D 软件中创建场景。
- 优化所有物体,因为我们需要干净的 geometry,并且只保留我们能看到的面。
- 对所有内容进行 UV unwrap(展 UV)。
- 把渲染烘焙到 texture 中。
- 导出场景和 texture。如果这样做,我们只会得到一张大的 texture。但是,如果有大量物体且我们想要更好的质量,也可以使用多张 texture。
- 把所有内容导入 Three.js,并把 texture 应用到 mesh 上。
作为额外奖励,我们还会在 Three.js 中添加一些细节,让场景更有生气,比如一个很酷的 portal 效果,周围有萤火虫漂浮。
Blender
我们将使用 Blender 来创建场景、进行烘焙并导出。
如果你还不了解 Blender,你应该先学习专门讲解它的上一课。假设你现在已经掌握了 Blender 的基础知识,接下来的步骤会稍微直接一些。尽管如此,每一部分都会被解释。慢慢来,确保在继续之前每一点都弄对。
如果你熟悉 Blender 以及 UV unwrapping 之类的概念,可以尝试脱离本课,创建你自己的场景。如果你不熟悉,就应该严格照步骤操作。然后,一旦项目结束,你就可以自己尝试,创建你梦想中的场景。
本课中,我将使用 Blender 的 2.92.0 版本,但你很可能会使用更新的版本。请留意界面或快捷键可能的变化。
初始设置
首先,我们创建一个新的 Blender 文件。默认设置就是我们在 Blender 那一课中定义的,但你可以使用任何你喜欢的设置。
如果你想要相同的设置,也可以使用初始文件中的 portal.blend 文件:
场景中已经存在一个 Point Light,参数如下。
如果参数与你的不同,可能你把 Eevee 用作了 Renderer Engine。你现在可以更改它。反正稍后我们也会修改它。
先把默认的立方体或场景中任何默认物体删除,但保留灯光。使用 X 删除物体。
你现在可以保存文件了。在本课剩余部分,记得定期保存。
地面
我们将从地面开始。
用 Shift + A 创建一个简单的平面。
缩放该平面,使其覆盖 4 个单位。你可以查看场景地面上的网格,看看 4 个单位是什么样的。另外,这些单位可以是任何你想要的。实际上,你可能会看到类似 4m 的东西,因为你的 Blender 配置为使用米。这无关紧要,因为 Blender 中的 4 个单位在 Three.js 中也会对应 4 个单位。
要这样做,你可以在创建它时出现的小菜单中更改其尺寸:
你也可以在创建后使用 S 来缩放它(可以使用 CTRL 来取整数值):
或者你可以用 TAB 进入 Edit Mode,然后用同样的方式缩放:
根据你使用的技术不同,结果实际上会略有差异。如果你在不使用 Edit Mode 的情况下缩放物体,你是在对物体本身应用变换,而不是对 geometry 应用。
这以后可能会成为一个问题,但之后我们无论如何都会为每个物体修复它。所以使用你喜欢的技术吧。
围栏
作为场景中的第一批物体,我们可以从围栏开始。它们易于创建,并能为场景增添一些生气。
我们将为柱子(posts)创建 2 个扁平的盒子,再为横板(rails)创建 2 个其他扁平且宽的盒子。
用 SHIFT + A 制作一个立方体来创建第一根柱子。用 TAB 进入 Edit Mode 并开始缩放它:
如果一开始没有得到确切的形状,不是问题。你随时可以之后更改。
现在,我们处于 Edit Mode,并且在这个围栏的剩余部分都会保持在该模式中。我们仍然可以创建基本体,它们会被算作同一 geometry 的一部分。这不是必须的,但每根围栏只有一个 geometry 可能会很方便。
在 Edit Mode 中时,用 A 选中盒子(如果还没选中的话),用 SHIFT + D 复制它,并把它移到一旁。你可以在复制后立即按 Y,强制只在 Y 轴上移动:
再次用 SHIFT + D 复制它,用 S 缩放它,用 G 移动它,来创建另外两块板:
现在你已经有了围栏的所有组件,可以按你喜欢的任何方式重新排列它。你也可以将整个 geometry 居中,使原点(那个小橙点)大致位于围栏底部的中心:
一旦你对结果满意,按 TAB 离开 Edit Mode:
你现在可以用 SHIFT + D 复制围栏,创建 6 根围栏。确保你处于 Object Mode,并给它们的位置和旋转添加一些随机性:
一如既往,不要在摆放东西上花太多时间。你随时可以在拥有所有物体之后再去做。
第一次渲染
现在我们已经有了一些可见的东西,可以尝试做第一次渲染了。即使最终目标是把场景导出到 Three.js,我们也想看看它在 Blender 中的样子,因为烘焙技术应该能给我们带来相同的结果。这也是创建炫酷渲染作品与社区或朋友分享的好机会。
Camera
用 SHIFT + A 创建一个 camera。
将鼠标悬停在其中一个 3D Viewport 区域(在我们的例子中是右下角),并选中 camera 时,按 NUMPAD 0。这会将 camera 定位,使你能在 viewport 中看到它所看到的内容。
从另一个 3D Viewport 区域选中 camera,用 G 移动它,使场景充满视野:
你现在可以按一次 Z 来显示 Shading 轮盘,把 camera 的 shading 更改为 Rendered:
现在你就能预览 camera 所看到的最终渲染结果了:
默认情况下,Blender 使用的 render engine 是 Eevee。Eevee 有点像 WebGL。它非常快,不使用 Ray Tracing,但看起来也不如另一个名为 Cycles 的 engine 好。
在为实时用途进行基础建模时,使用 Eevee 可能很有用。但在我们的情况中,我们想要最好看的结果,因此我们将切换到 Cycles。
在 Properties 区域,转到 Render Properties 标签页,选择 Cycles 作为 Render Engine:
camera 预览应该会显示更好看的结果:
质量
有很多参数可以控制质量,但最重要的一个是 Sampling。
再次转到同一个 Render Properties 标签页,查看 Sampling 部分:
值越高越好,但渲染所需的时间也越长。
128是一个不错的值,但你可能会看到一些 artifacts,它们就像渲染中的视觉噪点。256看起来会更好,噪点应该不会太明显。512对于获得好看的结果应该绰绰有余了。1024可以确保质量,但前提是你的电脑性能足够强。否则,你可能要等上几个小时。
显然,你可以使用任何中间值,比如 200 或任何其他数字,但我喜欢保持 2 的幂次方数字(开发者的条件反射)。
目前,我们将为 Render 保留 128,为 Viewport 保留 32。
分辨率
另一个参数是输出分辨率。
你可以在 Output Properties 标签页中访问此属性:
在这里你可以更改输出图像的宽度和高度,也可以对它进行缩放,以防你想做一个更小的渲染而不必计算宽度和高度。
让我们使用经典分辨率 1920x1080,不缩放(100%)。
第一次渲染
是时候进行我们的第一次渲染了。
按 F12 在新窗口中开始渲染。稍等片刻,你会看到结果一块一块地出现:
渲染完成后,在渲染窗口仍然打开时,按 ALT + S 保存图像。
在这里你可以选择图像格式、保存位置以及质量:
这部分由你决定,取决于你想要的图像质量。
树干
让我们继续我们的场景,创建一些树干。
因为我们想要创建 low poly 风格,所以我们将创建一些只有 10 个侧面的圆柱体。
用 SHIFT + A 创建一个 Cylinder,在应该出现在左下角的创建菜单中,选择 10 个顶点:
用 G 把树干移到离 camera 最近的一侧,用 S 调整它的大小,使其与围栏的尺寸相匹配:
用 SHIFT + D 复制它,并创建一堆不同高度的其他树干。
你可以通过在 Z 轴上移动圆柱体的顶面来变换 geometry 本身,或者对它进行缩放。
确保它们没有悬浮在地面上方。这些可不是魔法树干:
稍后,我们将创建一把卡在其中一根树干里的斧头。我们现在可以先在地上创建一些劈开的原木,来展示斧头做了什么。
再次创建一个圆柱体,但这次在创建窗口中,选择 Triangle Fan 作为 Cap Fill Type。
如果你切换到 Wireframe shading,你会看到圆柱体的顶部和底部现在变成了三角形:
这将使我们能够移除不需要的部分,最终只留下圆柱体的一截。
选中原木,用 tab 进入 Edit Mode,移除原木中你不需要的部分。在我们的例子中,我们可以保留它的两部分。按 X 移除你选中的面。
慢慢来,一部分一部分地移除。如果你做错了就撤销:
这是个不错的开始,但我们缺少面。
当你在 Edit Mode 中时,切换到 vertex select:
现在选中你想要创建的那个面的全部 4 个顶点:
然后按 F 用这 4 个顶点创建一个新的面:
对另一侧做同样操作。你应该能得到一块好看的原木:
调整原木的大小,离开 Edit Mode,把它复制一堆,散布在树干周围的地面上:
整理
目前,我们创建物体时并没有注意如何组织场景结构。
和一切事物一样,稍微整理一下是好的。
在 Blender 中,我们有称为 Collections 的东西。你可以看到它们就像文件夹,你可以把物体放进去。请注意,它们并不像 Three.js 的 groups 那样——你可以缩放 group,里面的所有物体会一起缩放。它更像是一种轻松组织物体的方式。
我们本可以使用 Blender 的父级系统来采用更经典的编组方案,但这样做可能会创建出复杂的场景图,可能会在后面的课程中破坏我们的代码。如果可能的话,尽量让场景图保持尽可能扁平。
在 Outliner 区域,你可以看到我们已经有两个 collections:
Scene Collection 是主 collection。你无法对它做太多操作。
Collection 是默认的那个,所有物体默认都会被添加进去。你可以重命名它,也可以使用它右侧的复选框来禁用它:
这会在 viewport 和渲染中都隐藏这些物体。
你可以通过添加其他按钮来获得更多控制。为此,点击 Outliner 右上角的 Filter 按钮:
然后激活以下按钮:
有了箭头图标,我们现在可以使某个 collection 不可选中但仍然可见。
我们现在还不打算重命名物体。相反,我们将创建 collections 并把物体放进去。
要创建新的 collection,你可以右键点击 Outliner 的空白部分,选择 New Collection,然后写入你想要的名字。
或者你可以在场景中选中你想要的物体,按 M,然后选择 New Collection(或选择一个现有的 collection):
如下所示整理你的 Outliner,并让 others collection 不可选中:
从现在起,我们将把所有物体整理到 collections 中。
你也可以在其他 collections 内部添加 collections。
Collections 在选中它包含的每个物体时也很有用。右键点击该 collection,选择 Select Objects:
斧头
让我们创建场景中一个虽小但更困难的部分——斧头。
我们暂时不需要按正确的比例来制作它。这样,我们就可以在更大的形状上工作。
我们将从斧头的头部开始。
头部
从一个扁平的立方体开始:
进入 Edit Mode,用 CTRL + R 添加一些环切(进行环切时,使用 WHEEL 来添加更多环):
移动顶点把底部变圆,把顶部缩小。这一步怎么做取决于你和你想要达到的形状,但尽量保持一个对称的形状。选中你想要更改的顶点,用 G 移动它们,用 S 缩放它们。别忘了,你可以在开始变换后按 X、Y 或 Z 来强制某个轴:
在切面中添加一条环切(CTRL + R):
选中以下顶点:
按两次 G,让它们沿各自的 edge 向上移动:
我们有了斧头。别忘了把它放进一个 collection:
手柄
我们现在可以创建手柄了。
从一个穿过斧头的盒子开始:
在 Edit Mode 中用 CTRL + R 创建环切并移动顶点,来添加一些弧度。你也可以通过选中面并按 E 来挤出,从而延伸手柄的尖端:
调整斧头大小,把它放在其中一根树干上,别忘了把手柄放进 axe collection:
路灯
对于路灯,使用与之前相同的技术。你应该能够自己处理这个。
别忘了把所有东西放进一个 poleLights collection。
创建底座:
添加横梁:
添加将悬挂灯具的绳子:
添加灯具本身:
我们想要在那个灯具的侧面创建一些凹槽。
在 Edit Mode 中选中灯具的 4 个侧面以及下面那个面:
按 I 创建一个内插(inset),并立即 LEFT CLICK,不要移动光标:
这样做,我们创建了一个内插,但偏移量是错的,因为我们在点击前没有移动鼠标。但如你所见,我们仍然可以用左下角打开的菜单来更改内插的参数。
勾选 Individual 并调整 Thickness 值。确保外框足够大,否则以后可能会出现问题:
我们现在想把这些面往里推。我们将做一次挤出,但首先,把 Transform Pivot Point 改为 Individual Origins:
这样,挤出将独立地应用于每个面。
按 E,并像内插一样,立即 LEFT CLICK,以便用应该出现在左下角的菜单来调整挤出参数。
将 Orientation 设为 Normal,并调整 Z 值:
你可以把 Transform Pivot Point 改回 Bounding Box Center:
这是一盏好看的灯,但我们还需要把这些面与外框分离。这以后会很方便。
确保在 Edit Mode 中内部的面仍处于选中状态,按 P 打开分离菜单。选择 Selection:
你应该会为这盏灯得到两个不同的物体。一个用于外框,一个用于以后会发出光的面。
选中整盏路灯并复制它,但这次使用 ALT + D 而不是 SHIFT + D。把它放在另一侧:
这会创建一个 Linked Duplicate。这样做,编辑一个 geometry 会自动编辑所有链接的 geometry。这样,保持这两盏路灯完全相同就更容易了。
现在是个保存并做一次渲染(F12)的好时机。
岩石
岩石有点棘手,但制作起来很有趣。我们将从立方体开始,随机地切割它们。
创建一个 rocks collection,并在里面创建一个立方体:
我们现在要切割那个立方体。
进入 Edit Mode 并选中所有面(如果还没选中的话)。
要切割立方体,我们可以使用 Bisect 工具。不幸的是,这个工具有点难找。
你可以通过按住 LEFT CLICK 点击左侧的 Tools 菜单中的图标来访问它,如下所示,然后选择 Bisect(如果你看不到左侧菜单,按 T):
你也可以在 3D Viewport 顶部的 Mesh 菜单中访问它:
或者你可以按 F3 并搜索 Bisect:
每次你想切割立方体时,都必须这样做。
现在 Bisect 已激活,点击并拖拽穿过立方体来创建一条切割线:
用左下角的菜单,你可以用 Clear Outer 清除立方体的一侧,并用 Fill 填充它产生的洞:
就这样,你完成了第一次干净的切割。
再次用 A 选中所有面,用随机的角度重复这个过程。试着打破最初形状的方正感,并对一堆其他立方体重复这个过程。
不要试图去精修顶点。你可能会破坏面的平整度。
你也应该尽量让 geometry 的原点保持在地面上。如果我们想缩放它们,这会很方便。
一如既往,慢慢来,不要在细节上花太多时间。
对 3 或 4 个立方体这样做:
现在你有了一堆岩石,复制 / 缩放 / 旋转它们,把它们在场景中到处移动。是时候填补空旷了。不要害怕重复使用相同的岩石。人们不会注意到的,即使注意到了,他们也会觉得很有趣。
在后面添加更多大岩石来营造一些高度,但不遮挡视野。在前面使用小岩石来添加细节:
Portal
是时候添加场景的主要组成部分——portal 了。
我们将从排成一个圆圈的砖块开始。
创建一个 portal collections,并把你的第一块砖添加进去:
然后,进入 Edit Mode,把砖块的 geometry 向下移动,让它的原点远远在上方:
离开 Edit Mode,使用 ALT + D 复制砖块,创建一个 Linked Duplicate(就像我们为路灯所做的),并在仍然处于复制模式时,把它绕 Y 轴旋转。
在验证复制之前进行旋转非常重要:
我们将重复这个操作。为此,按 F3 并搜索 repeat last:
这会重做上一个操作(即复制),并且由于我们在那次复制中也做了旋转,新的砖块也会旋转。多次这样做,直到你得到一个几乎完整的圆圈。如果旋转方向错了,没关系,我们可以多复制几块砖:
如果这些步骤太难,只需使用 ALT + D(而不是 SHIFT + D)复制砖块,然后手动放置它们。
旋转整个东西,使开口部分位于底部:
选中其中一块砖(如果可能的话,选择一块与场景对齐的,比如顶部那块),并进入 Edit Mode:
如你所见,所有砖块看起来都在被编辑。这是因为它们是链接的副本,编辑一个就会编辑其他的。
移动顶点,让砖块不再重叠:
离开 Edit Mode,缩放并移动整个 portal 到后面,略高于地面:
用盒子添加一些台阶,别忘了把所有东西放进一个名为 portal 的 collection:
我们需要填充那个 portal。
创建一个圆,并确保 Fill Type 为 Triangle Fan:
把它移到 portal 的中心。它不必完美,只要圆盘覆盖住洞口且不溢出即可。
要把它旋转到完美的 90 度角,在旋转时按一次 X(或 Y,取决于你的场景)来强制轴。你也可以在移动鼠标时按 CTRL 来取整数值:
建模部分就到这里了。
现在你有了所有元素,你可以改进整体设置,移动物体,让 camera 居中,移动 light,等等。
确保每个物体都妥善组织在 collections 中,别忘了保存。
材质
是时候给场景添加一些颜色了。
首先,确保场景中的灯光是白色的,且强度足够强。这样,你就能在中性环境中欣赏颜色了。我们稍后会对灯光进行一些更改。
你还应该把主 3D Viewport 更改为 Material Preview shading(按 Z),甚至 Renderer shading,以真正看到你在做什么:
草地
选中地面。在 Properties 区域,转到 Materials Properties 标签页,用 new 按钮添加一个 material:
你可以在 material 部分顶部把该 material 重命名为 grass:
然后你可以调整不同的属性。对于我们想要的风格,你只需更改 Base Color 并将 Roughness 设为最大值:
找到合适的颜色取决于你,但不要在这上面花太多时间。在调整之前,你需要看到带有所有颜色的整体场景。
木材
现在选中其中一根树干,创建一个名为 wood 的新 material。
顺便说一句,你的场景中可能已经有默认 material 了。通常,随场景创建的默认 material 名为 Material。如果你不喜欢有未使用的东西挂着,你可以使用那个 material 并更改它的名字:
要把这个 material 应用到场景中所有其他木质物体上,你不必逐个更改,可以通过右键点击 collection 并选择 Select objects 来选中它们:
这会选中 collection 中的所有物体:
确保带有那个 material 的物体是活动物体(轮廓更亮)。你可以使用 SHIFT + LEFT CLICK 来更改活动物体:
你现在可以按 CTRL + L 打开一个菜单,在其中你可以选择要把活动物体的什么内容应用到所有选中的物体上。这里,我们想应用 Materials:
所有选中的物体都应该拥有 wood material:
我们可以对围栏做同样的事。
- 选中所有围栏。
- 选中其中一根树干。
- 确保那根树干是活动物体。
- 按
CTRL + L并选择Materials。
所有围栏都应该应用了 wood material:
对路灯的木质部分以及斧头手柄重复这些步骤:
岩石
选中一块岩石,创建一个 rock material:
然后把这个 rock material 应用到所有岩石类的 material 上(岩石、portal 的砖块、portal 的台阶以及路灯的底座):
金属
灯的外框可以用一种更深的金属制作。
选中其中一个,并应用一个名为 metal 的 material:
其他的灯的外框会自动获得相同的 material,因为它们是链接的。
把同样的 metal material 应用到斧头:
发光灯光
我们希望 portal 和灯发出光。这可以通过一种名为 Emission 的 material 类型实现。
路灯
选中其中一盏灯(里面的那些面),创建一个新的 material。把它命名为 poleLightEmission,并且不用 Principled BSDF,而是为 Surface 属性选择 Emission:
这种 material 会发出光。
把它的 Color 改为更暖的色调,并把 Strength 提高到类似 80 的值:
Portal
创建另一个 Emission material,命名为 portalEmission。把 Strength 设为类似 40 的值,并使用你想要的颜色(我们之后可以更改它们):
我们看不到太多光,但如果你移除场景中所有其他的灯光,你会看到它运行得相当不错:
环境照明
我们可以添加的最后一项修饰是环境照明(ambient lighting),用来控制场景的整体照明。
如果你还没做,删除场景中的所有灯光。
然后,创建一个 Area Light,放大它,把它移到场景的左侧,并确保从灯延伸出去的那条垂直光线指向场景中心:
把它添加到 others collection。
Area Light 是一个矩形平面,会产生漫射光。平面越大,漫射越强。这有助于营造氛围,同时也会柔化阴影以获得更平滑的结果。
把灯的 Power 提高到类似 240 的值,并更改 Color:
最后调整
现在是调整颜色和照明的好时机。这里,用橙色的 Area Light,我们做出了旭日东升的效果:
但如果我们减弱 portal 的光,把 Area Light 的颜色改为蓝色,并调整场景中物体的颜色,我们就能创造出夜晚效果:
你也可以调整 portal 的颜色:
选择你最喜欢的设置。我们在后续课程中要做的事仍然会有效。
最终渲染
创建部分就到这里了。在下一课中,我们将学习如何优化、烘焙并导出所有内容。
在此期间,你可以创建并分享你的作品。要改进你的渲染,别忘了在 Render properties 标签页中提高 sampling。介于 512 和 1024 之间的某个值应该绰绰有余了:
在同一个部分,你还可以勾选 Denoise 属性并选择 NLM,从而消除视觉噪点: